using behaviac;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using System;
using System.Collections.Generic;

namespace IQIGame.Onigao.GamePlay
{
    /// <summary>
    /// 行为树管理器，各模块通过new BehaviacManager()创建自身模块的实例，注意手动调用OnUpdate实现帧刷
    /// </summary>
    public class BehaviacManager : IDisposable
    {
        private List<Agent> _agents = new List<Agent>();
        private List<Agent> _tempUpdateAgents = new List<Agent>();
        private float _updateElapsedTime;
        private float _updateInterval;

        public int id { get; private set; }

        public BehaviacManager(int id, float updateInterval)
        {
            this.id = id;
            this._updateInterval = updateInterval <= 0 ? 0 : updateInterval;
        }

        /// <summary>
        /// 添加行为树
        /// </summary>
        /// <typeparam name="T_AG">行为树代理类，继承BaseEntityAgent</typeparam>
        /// <param name="subject">被代理对象，实现IBehaviourSubject接口</param>
        /// <param name="btName">行为树配置相对路径。相对于根目录（PathConstant.BehaviacCfgRoot）。第一个字节不能是特殊字符(如/,.)，也不包含后缀，如：Level/Logic/EntityHeadToDes</param>
        public bool AddAgent<T_AG>(IBehaviourSubject subject, string btName) where T_AG : Agent, IBehaviourAgent, new()
        {
            if (string.IsNullOrEmpty(btName))
            {
                LogGame.LogError("btName is null.");
                return false;
            }
            if (subject.agent != null)
            {
                LogGame.LogError("Agent exists.");
                return false;
            }

            BehaviacGlobal.Instance.AddPath(id, PathConstant.GetBehaviacCfg(btName));

            Agent agent = ClassPool.Get<T_AG>();

            IBehaviourAgent iAgent = (IBehaviourAgent)agent;
            iAgent.SetSubject(subject);
            subject.agent = iAgent;

            if (agent.btload(btName))
            {
                iAgent.btName = btName;
                agent.btsetcurrent(btName);
                _agents.Add(agent);
                return true;
            }
            else
            {
                LogGame.LogError($"行为树 {btName} 加载失败，请检查是否删除了行为树，并重新导出相应配置（如关卡编辑器配置）以刷新相关引用。");
                return false;
            }
        }

        /// <summary>
        /// 移除行为树
        /// </summary>
        /// <param name="subject"></param>
        public void RemoveAgentFrom(IBehaviourSubject subject)
        {
            if (subject == null)
            {
                LogGame.LogError("Parameter is null.");
                return;
            }
            IBehaviourAgent iAgent = subject.agent;
            if (iAgent == null)
            {
                return;
            }
            Agent agent = (Agent)iAgent;
            agent.btunload(iAgent.btName);
            _agents.Remove(agent);
            ClassPool.PutAutoType(iAgent);
            subject.agent = null;
        }

        /// <summary>
        /// 改变行为树
        /// 泛型NEW_AG的类型可与原Agent一致，只要行为树配置即newBtName发生变化，一样可以调用改接口
        /// </summary>
        /// <typeparam name="NEW_AG"></typeparam>
        /// <param name="subject">被代理的对象，一般为各模块的EntityObject，其本身的行为树可为空，为空则不执行移除原有行为树的逻辑</param>
        /// <param name="newBtName">新的行为树名称，可为空，为空仅移除</param>
        public bool ChangeAgent<NEW_AG>(IBehaviourSubject subject, string newBtName) where NEW_AG : Agent, IBehaviourAgent, new()
        {
            RemoveAgentFrom(subject);
            if (string.IsNullOrEmpty(newBtName))
            {
                return false;
            }
            return AddAgent<NEW_AG>(subject, newBtName);
        }

        public void OnUpdate(float deltaTime)
        {
            if (_updateInterval > 0)
            {
                _updateElapsedTime += deltaTime;
            }
            if (_updateInterval <= 0 || _updateElapsedTime >= _updateInterval)
            {
                _updateElapsedTime -= _updateInterval;
                foreach (Agent agent in _agents)
                {
                    _tempUpdateAgents.Add(agent);
                }
                foreach (Agent agent in _tempUpdateAgents)
                {
                    IBehaviourAgent iAgent = agent as IBehaviourAgent;
                    if (iAgent.subject.isBehavTreeActive)
                    {
                        EBTStatus status = agent.btexec();
                        if (status == EBTStatus.BT_INVALID)
                        {
                            LogGame.LogError($"Agent {agent.GetName()} execution invalid.");
                        }
                    }
                }
                _tempUpdateAgents.Clear();
            }
        }

        private void ClearAgents()
        {
            id = 0;
            while (_agents.Count > 0)
            {
                RemoveAgentFrom(((IBehaviourAgent)_agents[0]).subject);
            }
        }

        public void Dispose()
        {
            ClearAgents();
        }
    }
}
